//
// Created by User on 24-10-3.
//

#include <glog/logging.h>

#include "general_demuxer.h"
#include "stream.h"
#include <filesystem>

extern "C"
{
#include <libavformat/avformat.h>
}

namespace demuxer
{
    using namespace std;

    general_demuxer::general_demuxer(const std::string& path)
    {
        try
        {
            this->init_demuxer(path);
        }
        catch (error::demuxer_error& error)
        {
            this->clean_demuxer(error.get_type());
            throw;
        }
        catch (...)
        {
            this->clean_demuxer(error::demuxer_error::type::UNKNOWN_ERROR);
            throw;
        }
    }

    general_demuxer::~general_demuxer()
    {
        this->clean_demuxer(error::demuxer_error::type::UNKNOWN_ERROR);
    }

    utils::media_packet general_demuxer::get_packet(int stream_index)
    {
        auto& que = this->m_queues[stream_index];
        if (que.empty() && this->is_end)
        {
            return {};
        }
        if (que.size() < 10 && !this->is_end)
        {
            this->read_frame(que);
        }
        auto temp = que.front();
        que.pop();
        return temp;
    }

    auto general_demuxer::get_streams() -> std::vector<std::unique_ptr<interfaces::packet_provider> >&
    {
        return this->m_streams;
    }

    void general_demuxer::init_demuxer(const std::string& path)
    {
        // // 检查路径合法性
        // if (!filesystem::is_regular_file(path))
        // {
        //     throw error::demuxer_error(error::demuxer_error::type::PATH_ERROR, "can not open file");
        // }
        //路径合法，打开文件
        int ret = avformat_open_input(&this->m_fmt_ctx, path.c_str(), nullptr, nullptr);
        if (ret < 0)
        {
            throw error::demuxer_error(error::demuxer_error::type::FILE_OPEN_ERROR, ret);
        }

        //解析流信息
        ret = avformat_find_stream_info(this->m_fmt_ctx, nullptr);
        if (ret < 0)
        {
            throw error::demuxer_error(error::demuxer_error::type::FIND_STREAM_ERROR, ret);
        }

        //打开各个流
        for (int i = 0; i < this->m_fmt_ctx->nb_streams; i++)
        {
            this->m_queues.emplace_back();
            switch (this->m_fmt_ctx->streams[i]->codecpar->codec_type)
            {
                case AVMEDIA_TYPE_VIDEO:
                {
                    LOG(INFO) << "read stream : " << this->m_fmt_ctx->streams[i]->index << "\t type : video";
                    this->m_streams.emplace_back(make_unique<video_stream>(*this, this->m_fmt_ctx->streams[i]));
                }
                break;
                case AVMEDIA_TYPE_AUDIO:
                {
                    LOG(INFO) << "read stream : " << this->m_fmt_ctx->streams[i]->index << "\t type : audio";
                    this->m_streams.emplace_back(make_unique<audio_stream>(*this, this->m_fmt_ctx->streams[i]));
                }
                break;
                case AVMEDIA_TYPE_SUBTITLE:
                    //TODO :补充字幕流内容
                    break;
                default:
                    ;
            }
        }

        // 初始化pkt
        this->m_pkt = av_packet_alloc();
        if (!this->m_pkt)
        {
            throw error::demuxer_error(error::demuxer_error::type::PACKET_ALLOC_ERROR, "can not alloc packet");
        }
    }

    void general_demuxer::clean_demuxer(error::demuxer_error::type _type)
    {
        switch (_type)
        {
            case error::demuxer_error::type::UNKNOWN_ERROR:
                av_packet_free(&this->m_pkt);
            case error::demuxer_error::type::PACKET_ALLOC_ERROR: ;
            case error::demuxer_error::type::FIND_STREAM_ERROR:
                avformat_close_input(&this->m_fmt_ctx);
            case error::demuxer_error::type::FILE_OPEN_ERROR: ;
            case error::demuxer_error::type::PATH_ERROR: ;
            default: ;
        }
    }

    void general_demuxer::read_frame(std::queue<utils::media_packet>& que)
    {
        while (que.size() < 10 && !this->is_end)
        {
            int ret = av_read_frame(this->m_fmt_ctx, this->m_pkt);
            if (AVERROR_EOF == ret)
            {
                this->is_end = true;
                break;
            }
            if (ret < 0)
            {
                throw error::demuxer_error(error::demuxer_error::type::READ_FRAME_ERROR, ret);
            }
            this->m_queues[this->m_pkt->stream_index].emplace(this->m_pkt);
            av_packet_unref(this->m_pkt);
        }
    }
} // demuxer
